#ifndef __M_MATHCH_H__
#define __M_MATHCH_H__

#include <list>
#include <mutex>
#include <condition_variable>
#include <pthread.h>

#include "room.hpp"
#include "online_user.hpp"
#include "db.hpp"

template <class T>
class match_queue
{
public:
    /* 获取队列中元素个数 */
    size_t size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    /* 判断队列是否为空 */
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    /* 阻塞线程 */
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }

    /* 入队数据并唤醒线程 */
    void push(const T &data = T())
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }

    /* 队头入数据——针对匹配成功的一个玩家掉线，另一个在线的玩家要重新入队且保持最大优先级 */
    void push_front(const T &data = T())
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_front(data);
        _cond.notify_all();
    }

    /* 出队数据 */
    bool pop(T *out)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty())
            return false;

        *out = _list.front();
        _list.pop_front();
        return true;
    }
    /* 移除指定数据 */
    void erase(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }

private:
    /* 使用list而非queue是因为有中间删除数据的需要 */
    std::list<T> _list;            // 阻塞队列
    std::mutex _mutex;             // 互斥锁 保证线程安全
    std::condition_variable _cond; // 条件变量，主要是为了阻塞消费者 当队列中玩家数量<2时便阻塞消费者
};

class match_manage
{
public:
    match_manage(room_manager *rm, user_table *ut, online_user *olu) : 
    _rm(rm), _ut(ut), _olu(olu),
    _th_primary(std::thread(&match_manage::th_primary_entry, this)),
    _th_middle(std::thread(&match_manage::th_middle_entry, this)),
    _th_senior(std::thread(&match_manage::th_senior_entry, this))
    {
        DBG_LOG("游戏匹配模块初始化完毕！");
    }
    ~match_manage() {}
    /* 添加用户到匹配队列 */
    void add_user(const uint64_t uid)
    {
        // 1. 根据用户id获取玩家信息
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DBG_LOG("获取玩家[uid:%d]信息失败！", uid);
            return;
        }
        // 2. 根据玩家信息获取玩家天梯分数
        // 3. 用户入队
        int score = user["score"].asInt();
        if (score <= 2000)
            _q_primary.push(uid);
        else if (score > 2000 && score <= 3000)
            _q_middle.push(uid);
        else if (score > 3000)
            _q_senior.push(uid);
    }
    /* 从匹配队列中移除玩家 */
    bool remove_user(const uint64_t uid)
    {
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DBG_LOG("获取玩家[uid:%ld]信息失败！", uid);
            return false;
        }
        int score = user["score"].asInt();
        if (score <= 2000)
            _q_primary.erase(uid);
        else if (score > 2000 && score <= 3000)
            _q_middle.erase(uid);
        else if (score > 3000)
            _q_senior.erase(uid);

        return true;
    }

private:
    void handle_match(match_queue<uint64_t> &match_q)
    {
        while (true)
        {
            // 1. 判断队列人数是否不小于2，小于2则阻塞等待
            while (match_q.size() < 2)
            {
                match_q.wait();
            }
            // 2. 出队列两个玩家
            uint64_t uid1, uid2;
            bool ret = match_q.pop(&uid1);
            if (ret == false)
                continue;
            ret = match_q.pop(&uid2);
            if (ret == false)
            {
                // 出队失败，将另一个已经出队的玩家重新入队，且保持原来的优先级
                match_q.push_front(uid1);
                continue;
            }
            // 3. 判断两个玩家是否都在线（可能匹配的时候掉线了），若有一个玩家掉线，则需将另一个在线的再次加入队列，且保持原来的优先级
            wsserver_t::connection_ptr conn1 = _olu->get_conn_from_hall(uid1);
            if (conn1.get() == nullptr)
            {
                match_q.push_front(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _olu->get_conn_from_hall(uid2);
            if (conn2.get() == nullptr)
            {
                match_q.push_front(uid1);
                continue;
            }
            // 4. 两个玩家都在线，为其创建房间，并将玩家加入到房间中
            room_ptr rmp = _rm->create_room(uid1, uid2);
            // 创建房间失败，将玩家重新加入到匹配队列队首
            if (rmp.get() == nullptr)
            {
                match_q.push_front(uid1);
                match_q.push_front(uid2);
                continue;
            }
            // 5. 对玩家响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp, &body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    /* 线程入口函数 */
    void th_primary_entry()
    {
        handle_match(_q_primary);
    }
    void th_middle_entry()
    {
        handle_match(_q_middle);
    }
    void th_senior_entry()
    {
        handle_match(_q_senior);
    }

private:
    match_queue<uint64_t> _q_primary; // 初级匹配队列
    match_queue<uint64_t> _q_middle;  // 中级匹配队列
    match_queue<uint64_t> _q_senior;  // 高级匹配队列

    std::thread _th_primary; // 对匹配队列处理的线程
    std::thread _th_middle;
    std::thread _th_senior;
    room_manager *_rm; // 房间管理模块的操作句柄
    user_table *_ut;
    online_user *_olu; // 在线用户管理模块的操作句柄
};

#endif